package znet

import (
	"fmt"
	"zinx/utils"
	"zinx/ziface"
)

/**
消息处理模块实现
*/

type MsgHandle struct {
	// 存放msgID对应的处理方法
	Apis map[uint32]ziface.IRouter
	// 负责Worker取任务的消息队列
	TaskQueue []chan ziface.IRequest
	// 业务工作Worker池的worker数量
	WorkerPoolSize uint32
}

// 初始化创建MsgHandle方法
func NewMsgHandle() *MsgHandle {
	return &MsgHandle{
		Apis:           make(map[uint32]ziface.IRouter),
		WorkerPoolSize: utils.GlobalObject.WorkerPoolSize, // 从全局配置中读取
		TaskQueue:      make([]chan ziface.IRequest, utils.GlobalObject.WorkerPoolSize),
	}
}

// 调度执行对应的Router消息处理方法
func (mh *MsgHandle) DoMsgHandler(request ziface.IRequest) {
	// 从request中找到msgId
	handler, ok := mh.Apis[request.GetMsgID()]
	if !ok {
		fmt.Println("api msgID = ", request.GetMsgID(), "is NOT ! need Register")
	}
	// 2.根据msgID调度对应的router业务
	handler.PreHandle(request)
	handler.Handler(request)
	handler.PostHandler(request)
}

// 添加具体的处理逻辑
func (mh *MsgHandle) AddRouter(msgID uint32, router ziface.IRouter) {
	// 1.判断当前msg绑定的Api处理方法是否存在
	if _, ok := mh.Apis[msgID]; ok {
		// msgId已注册
		panic("1")
	}
	// 2. 添加msg与Api对应的关系
	mh.Apis[msgID] = router
}

// 启动一个worker工作池(开启工作池的动作只能发生一次，一个zinx框架只能有一个工作池)
func (mh *MsgHandle) StartWorkerPool() {
	//根据workerPoolSize分别开启Worker，每个Worker用一个go承载
	for i := 0; i < int(mh.WorkerPoolSize); i++ {
		// 一个Worker被启动
		// 1.当前的的worker对应的channel消息队列 开辟空间 第0个worker 就用第0个channel
		mh.TaskQueue[i] = make(chan ziface.IRequest, utils.GlobalObject.MaxWorkerTaskLen)
		// 2.启动当前的worker,阻塞等待消息从channel传递进来
		go mh.StartOneWorker(i, mh.TaskQueue[i])
	}

}

// 启动一个worker工作流程
func (mh *MsgHandle) StartOneWorker(workerID int, taskQueue chan ziface.IRequest) {
	fmt.Println("WorkerID =", workerID, "is started...")
	// 不断的阻塞等待对应消息队列的消息
	for true {

		select {
		// 如果有消息过来，出列的就是一个客户端的Request，执行当前Request所绑定的业务
		case requests := <-taskQueue:
			mh.DoMsgHandler(requests)
		}
	}
}

// 将消息交由TaskQueue,由Worker进行处理
func (mh *MsgHandle) SendMsgToTaskQueue(request ziface.IRequest) {
	//1.将消息平均分配给不同的worker
	// 根据客户端建立的ConnID来进行分配
	workerID := request.GetConnection().GetConnID() % mh.WorkerPoolSize
	fmt.Println("Add ConnID = ", request.GetConnection().GetConnID(), "request MsgID=", request.GetMsgID(), "to WorkerID = ", workerID)

	// 2.将消息发送给对应的worker的TaskQueue即可
	mh.TaskQueue[workerID] <- request

}
